﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using MiscUtil;
using TlvLib.SiChuan.Packet;

namespace TlvLib.SiChuan
{
    class ReceiveHandler
    {
        private readonly ConcurrentDictionary<UInt32, CarInfo> _carInfos;
        private Stream _stream;
        private Thread _thread;
        private CancellationTokenSource _cts;
        private NLog.Logger _logger = NLog.LogManager.GetCurrentClassLogger();

        public event EventHandler ConnectionClosed;
        public event EventHandler<EventArgs<Exception>> ErrorOccurred;

        public void OnErrorOccurred(EventArgs<Exception> e)
        {
            EventHandler<EventArgs<Exception>> handler = ErrorOccurred;
            if (handler != null) handler(this, e);
        }


        public void OnConnectionClosed(EventArgs e)
        {
            EventHandler handler = ConnectionClosed;
            if (handler != null) handler(this, e);
        }

        public ReceiveHandler(ConcurrentDictionary<uint, CarInfo> carInfos)
        {
            if (carInfos == null) throw new ArgumentNullException("carInfos");

            _carInfos = carInfos;
        }

        public void Start(Stream stream)
        {
            if (stream == null) throw new ArgumentNullException("stream");
            _stream = stream;

            if (_thread == null)
            {
                _cts = new CancellationTokenSource();
                _thread = new Thread(Worker);
                _thread.IsBackground = true;
                _thread.Start();
            }
        }

        public void Stop()
        {
            if (_cts != null)
            {
                _cts.Cancel();
            }
        }

        private void Worker(object o)
        {
            while (true)
            {

                if (_cts.IsCancellationRequested)
                {
                    break;
                }

                byte[] buffer = null;

                try
                {
                    buffer = TlvReader.Read(_stream);
                }
                catch (System.IO.IOException ex)
                {
                    _logger.ErrorException("接收数据出现异常， 连接可能已经关闭", ex);
                    OnConnectionClosed(EventArgs.Empty);
                    break;
                }

                try
                {
                    Packet.DataReportReply reply;
                    DateTime dateTime;
                    if (Unpacker.DataReportReplyUnpacker.TryUnpack(buffer, out reply))
                    {

                        if (reply.Result == DataReportResult.Success)
                        {
                            _logger.Info("车牌SN:{0}发送成功", reply.SN);
                            RemoveHistoryCarInfo(reply.SN);
                        }
                        else
                        {
                            CarInfo info;
                            var msg = "车牌发送失败";
                            if (_carInfos.TryGetValue(reply.SN, out info))
                            {
                                msg += string.Format("CDBH:{0}, CTHP:{1}", info.CDBH, info.CTHPHM);
                            }
                            
                            _logger.Info(msg);
                        }

                    }
                    else if (Unpacker.TimeSyncReplyUnpacker.TryUnpack(buffer, out dateTime))
                    {
                        SetDateTime(dateTime.ToUniversalTime());
                    }
                }
                catch (Exception ex)
                {
                    _logger.ErrorException("处理包时发生异常", ex);
                    OnErrorOccurred(new EventArgs<Exception>(ex));
                }


            }
        }

        private void SetDateTime(DateTime dateTime)
        {
            var st = new SYSTEMTIME();
            st.wYear = (short)dateTime.Year; // must be short
            st.wMonth = (short)dateTime.Month;
            st.wDay = (short)dateTime.Day;
            st.wHour = (short)dateTime.Hour;
            st.wMinute = (short)dateTime.Minute;
            st.wSecond = (short)dateTime.Second;

            SetSystemTime(ref st);
        }

        private void RemoveHistoryCarInfo(UInt32 sn)
        {
            if (_carInfos.ContainsKey(sn))
            {
                CarInfo info;
                _carInfos.TryRemove(sn, out info);

            }
        }


        [StructLayout(LayoutKind.Sequential)]
        public struct SYSTEMTIME
        {
            public short wYear;
            public short wMonth;
            public short wDayOfWeek;
            public short wDay;
            public short wHour;
            public short wMinute;
            public short wSecond;
            public short wMilliseconds;
        }

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern bool SetSystemTime(ref SYSTEMTIME st);


    }
}
